using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using System.Reflection;
using GriffinNav.Map.VirtualEarth;
using GriffinNav.GPS;
using GriffinNav.Map;
using Microsoft.WindowsCE.Forms;

namespace GriffinNav.UI
{
    public partial class frmViewer : Form
    {
        private SolidBrush mBackgroundBrush = new SolidBrush(Color.LightGray);
        private ImageAttributes mImgAttr = null;
        private ScreenOrientation mScreenOrientation = SystemSettings.ScreenOrientation;
        private MapControls mMapControls = null;

        private bool mShowPositionArrow = false;
        private Bitmap mPositionArrow = null;
        private Arrow mArrow = null;

        public frmViewer()
        {
            InitializeComponent();
            Text = Global.Name;

            try
            {
                mMapControls = new MapControls(ClientRectangle);

                mImgAttr = new ImageAttributes();
                mImgAttr.SetColorKey(Color.White, Color.White);
            }
            catch (Exception ex)
            {
                Global.ShowError("Error", ex.Message);
            }
        }

        private void frmViewer_Load(object sender, EventArgs e)
        {
            try
            {
                // check app state for a previously opened map
                if (!string.IsNullOrEmpty(AppState.LastMapOpened) &&
                    File.Exists(AppState.LastMapOpened))
                {
                    Cursor.Current = Cursors.WaitCursor;

                    // open the previously opened map
                    openMap(AppState.LastMapOpened);
                }
            }
            catch (Exception ex)
            {
                Global.ShowError("Error", ex.Message);
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            try
            {
                if (Global.Map != null)
                {
                    if (Global.Gps != null && Global.Gps.GpsState == DeviceState.Running &&
                        mArrow != null && mShowPositionArrow)
                    {
                        mPositionArrow = mArrow.DirectionArrow;
                        Global.Map.DrawScreen(mArrow.DirectionArrow, mArrow.ScreenPosition);
                    }
                    else
                    {
                        Global.Map.DrawScreen();
                    }

                    Global.Map.ScreenGraphics.DrawImage(mMapControls.ZoomIn.Image,
                         mMapControls.ZoomIn.ScreenPosition, 0, 0,
                         mMapControls.ZoomIn.ScreenPosition.Width,
                         mMapControls.ZoomIn.ScreenPosition.Height,
                         GraphicsUnit.Pixel, mImgAttr);
                    Global.Map.ScreenGraphics.DrawImage(mMapControls.ZoomOut.Image,
                        mMapControls.ZoomOut.ScreenPosition, 0, 0,
                        mMapControls.ZoomOut.ScreenPosition.Width,
                        mMapControls.ZoomOut.ScreenPosition.Height,
                        GraphicsUnit.Pixel, mImgAttr);

                    e.Graphics.DrawImage(Global.Map.ScreenBuffer, 0, 0);
                }
                else
                {
                     // draw gray background
                    e.Graphics.FillRectangle(mBackgroundBrush, ClientRectangle);
                }
            }
            catch (Exception ex)
            {
                Global.ShowError("Error", ex.Message);
            }
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            // do nothing
        }

        public void Gps_GpsStopped()
        {
            if (InvokeRequired)
            {
                try
                {
                    GenericEventHandler d = new GenericEventHandler(Gps_GpsStopped);
                    Invoke(d);
                }
                catch (ObjectDisposedException) { }
            }
            else
            {
                mArrow = null;
                mShowPositionArrow = false;
            }
        }

        public void Gps_PositionChanged(Position position)
        {
            if (InvokeRequired)
            {
                try
                {
                    PositionChangedHandler d = new PositionChangedHandler(Gps_PositionChanged);
                    this.Invoke(d, new object[] { position });
                }
                catch (ObjectDisposedException) { }

            }
            else
            {
                try
                {
                    if (Global.Map != null && position.FixQuality != FixQuality.Invalid)
                    {
                        // mArrow != null and mShowPositionArrow == true here
                        if (Global.Map.GoToPosition((double)position.Longitude.Coordinate, (double)position.Latitude.Coordinate))
                        {
                            if (mArrow == null)
                            {
                                mArrow = new Arrow(ClientRectangle);
                            }

                            // this code executes
                            mShowPositionArrow = true;
                            Invalidate();
                        }
                        else
                        {
                            mShowPositionArrow = false;
                        }
                    }
                }
                catch (Exception ex)
                {
                    Global.ShowError("Error", ex.Message);
                }
            }
        }

        public void Gps_VelocityChanged(Velocity velocity)
        {
            if (InvokeRequired)
            {
                try
                {
                    VelocityChangedHandler d = new VelocityChangedHandler(Gps_VelocityChanged);
                    this.Invoke(d, new object[] { velocity });
                }
                catch (ObjectDisposedException) { }
            }
            else
            {
                try
                {
                    if (mShowPositionArrow && mArrow != null)
                    {
                        if (mArrow == null)
                        {
                            mArrow = new Arrow(ClientRectangle);
                        }

                        // this code executes
                        mArrow.Direction = velocity.Direction;

                        Invalidate();
                    }
                }
                catch (Exception ex)
                {
                    Global.ShowError("Error", ex.Message);
                }
            }
        }

        private void Map_MapViewUpdated()
        {
            if (InvokeRequired)
            {
                try
                {
                    GenericEventHandler d = new GenericEventHandler(Map_MapViewUpdated);
                    Invoke(d);
                }
                catch (ObjectDisposedException) { }
            }
            else
            {
                Cursor.Current = Cursors.Default;

                Invalidate();
            }
        }

        private void frmViewer_KeyDown(object sender, KeyEventArgs e)
        {
            try
            {
                if (Global.Map != null && (Global.Gps == null || Global.Gps.GpsState != DeviceState.Running))
                {
                    if (e.KeyData == Keys.Right)
                    {
                        Global.Map.MoveRight();
                    }
                    else if (e.KeyData == Keys.Left)
                    {
                        Global.Map.MoveLeft();
                    }
                    else if (e.KeyData == Keys.Down)
                    {
                        Global.Map.MoveDown();
                    }
                    else if (e.KeyData == Keys.Up)
                    {
                        Global.Map.MoveUp();
                    }
                }
            }
            catch (Exception ex)
            {
                Global.ShowError("Error", ex.Message);
            }
        }

        private void frmViewer_MouseUp(object sender, MouseEventArgs e)
        {
            if (Global.Map != null)
            {
                if (mMapControls.ZoomIn.ScreenPosition.Contains(e.X, e.Y))
                {
                    // zoom in
                    if (Global.Map.ZoomIn())
                    {
                        Cursor.Current = Cursors.WaitCursor;
                    }
                }

                if (mMapControls.ZoomOut.ScreenPosition.Contains(e.X, e.Y))
                {
                    // zoom out
                    if (Global.Map.ZoomOut())
                    {
                        Cursor.Current = Cursors.WaitCursor;
                    }

                }

                mMapControls.ZoomIn.ButtonPosition = ButtonPosition.Up;
                mMapControls.ZoomOut.ButtonPosition = ButtonPosition.Up;

                Invalidate();
            }
        }

        private void frmViewer_MouseDown(object sender, MouseEventArgs e)
        {
            if (Global.Map != null)
            {
                if (mMapControls.ZoomIn.ScreenPosition.Contains(e.X, e.Y))
                {
                    mMapControls.ZoomIn.ButtonPosition = ButtonPosition.Down;
                    Invalidate();
                }

                if (mMapControls.ZoomOut.ScreenPosition.Contains(e.X, e.Y))
                {
                    mMapControls.ZoomOut.ButtonPosition = ButtonPosition.Down;
                    Invalidate();
                }
            }
        }

        private void frmViewer_Resize(object sender, System.EventArgs e)
        {
            try
            {
                if (SystemSettings.ScreenOrientation != mScreenOrientation)
                {
                    mScreenOrientation = SystemSettings.ScreenOrientation;

                    if (Global.Map != null)
                    {
                        Global.Map.ScreenRectangle = ClientRectangle;

                        mMapControls.ScreenRectangle = ClientRectangle;

                        if (mArrow != null)
                        {
                            mArrow.ScreenRectangle = ClientRectangle;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Global.ShowError("Error", ex.Message);
            }
        }

        private void mnuMain_Menu_ZoomIn_Click(object sender, EventArgs e)
        {
            try
            {
                if (Global.Map != null && Global.Map.ZoomIn())
                {
                    Cursor.Current = Cursors.WaitCursor;
                }
            }
            catch (Exception ex)
            {
                Global.ShowError("Error", ex.Message);
            }
        }

        private void mnuMain_Menu_ZoomOut_Click(object sender, EventArgs e)
        {
            try
            {
                if (Global.Map != null && Global.Map.ZoomOut())
                {
                    Cursor.Current = Cursors.WaitCursor;
                }
            }
            catch (Exception ex)
            {
                Global.ShowError("Error", ex.Message);
            }
        }

        private void mnuMain_Menu_OpenMap_Click(object sender, EventArgs e)
        {
            try
            {
                if (!string.IsNullOrEmpty(AppState.LastMapOpened) &&
                    Directory.Exists(Path.GetDirectoryName(AppState.LastMapOpened)))
                {
                    dlgOpenMap.InitialDirectory = Path.GetDirectoryName(AppState.LastMapOpened);
                }
                else
                {
                    dlgOpenMap.InitialDirectory = Global.AppPath;
                }

                DialogResult result = dlgOpenMap.ShowDialog();

                if (result == DialogResult.OK)
                {
                    Cursor.Current = Cursors.WaitCursor;

                    openMap(dlgOpenMap.FileName);

                    AppState.LastMapOpened = dlgOpenMap.FileName;
                }

                Invalidate();
            }
            catch (Exception ex)
            {
                Cursor.Current = Cursors.Default;
                Global.ShowError("Error", ex.Message);
            }
        }

        private void openMap(string path)
        {
            if (Global.Map != null)
            {
                Global.Map.Open(path, ClientSize);
            }
            else
            {
                Global.Map = new GriffinNav.Map.Map(path, ClientRectangle);
                Global.Map.MapViewUpdated += new GenericEventHandler(Map_MapViewUpdated);
                Global.Map.OnMapError += new MapErrorHandler(Map_OnMapError);
            }
        }

        public void Map_OnMapError(MapException ex, bool showMessage)
        {
            if (showMessage)
            {
                Global.ShowError("Error", ex.Message);
            }
        }

        private void mnuMain_Exit_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void mnuMain_Menu_Options_Click(object sender, EventArgs e)
        {
            try
            {
                Global.Options.ShowDialog(OptionsTab.General);
            }
            catch (Exception ex)
            {
                Global.ShowError("Error", ex.Message);
            }
        }

        private void mnuMain_Gps_Click(object sender, EventArgs e)
        {
            bool reInit = false;

            Global.GpsInfo.ShowDialog(out reInit);

            // check if GPS was started
            if (reInit)
            {
                // GPS was re-initialized and started, subscribe to events
                Global.Gps.GpsStopped += new GenericHandler(Gps_GpsStopped);
                Global.Gps.PositionChanged += new PositionChangedHandler(Gps_PositionChanged);
                Global.Gps.VelocityChanged += new VelocityChangedHandler(Gps_VelocityChanged);
                Global.Gps.OnGpsError += new GpsErrorHandler(Global.Gps_OnGpsError);
            }
        }

        private void mnuMain_Menu_About_Click(object sender, EventArgs e)
        {
            Global.About.ShowDialog();
        }

        private void frmViewer_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (Global.Gps != null && Global.Gps.GpsState == DeviceState.Running)
            {
                Global.Gps.StopGps();

                Thread.Sleep(100);
            }
        }
    }
}